package ufcg.metci.huffman;


import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Stack;

import ufcg.metci.util.FileControl;

/**
 * Projeto para disciplina de Metodologia Científica - UFCG - 2012.1
 * 
 * @author Ana Clara Lacerda
 * @author Rodolfo Marinho
 *
 * Classe que implementa o algoritmo de Huffman para a descompressão dos dados
 */
public class HuffmanDecompress extends Huffman{

	private String phrase;
	private String decompressedContent = "";
	HuffmanNode rootNode;

	public static void main(String[] args) {
		// Parametros input e output
		String inputFileName = "teste.huf";
		String outputFileName = "teste2";
		
		String content = null;
		try {
			content = FileControl.read(inputFileName);
			String contentDecompressed = new HuffmanDecompress().decompressFile(content);
			FileControl.saveDecompressFile(contentDecompressed, outputFileName, recoverExtension(content));
		} catch (FileNotFoundException e) {
			// TODO stderr
		} catch (IOException e) {
			// TODO stderr
		}
	}
	
	private static String recoverExtension(String content) {
		String ext = ((Character)content.charAt(0)).toString();
		int extensionSize = Integer.parseInt(ext);
		String extension = content.substring(1, extensionSize + 1);
		return extension;
	}
	
	/**
	 * @param content
	 */
	public String decompressFile(String content) {
		
			String contentPart = "";

			// Retira a extensao do incio da frase
			Character cExt = content.charAt(0);
			String sExt = cExt.toString();
			int tamanhoExt = Integer.parseInt(sExt) + 1;

			// Filtra o que é conteúdo e o que são informações adicionais
			for (int i = tamanhoExt; i < content.length(); i++) {
				String aux = content.substring(i, i + 6);
				if (aux.equals("-D-AFW")) {
					contentPart = content.substring(i + 6, content.length());
					content = content.substring(tamanhoExt, i);
					break;
				}
			}

			// Transforma frase em valores binarios
			this.phrase = recoverBinaryContent(content);
			// Recupera os valores e frequencias dos simbolos originais
			int[] freq = recoverDict(contentPart);

			// Constroi a arvore e gera os codigos dos simbolos
			rootNode = buildTree(freq);
			buildCodes(rootNode, new Stack<Byte>());

			return buildContent();
	}
	
	/**
	 * Recupera o dicionario de dados do proprio arquivo comprimido
	 * 
	 * @param dictContent
	 * @return
	 */
	private int[] recoverDict(String dictContent) {
		int[] frequencyArray = new int[256];

		if (dictContent != null && !dictContent.equals("")) {
			String[] split = dictContent.split("\\|{2}"); // Faz um split com o
															// separador "||"
			for (int i = 0; i < split.length; i++) {
				String word = split[i];
				String valor = ((Character)word.charAt(0)).toString();
				int frequency = Integer.parseInt(word.substring(1));
				frequencyArray[valor.toCharArray()[0]] = frequency;
			}
		}
		
		return frequencyArray;
	}

	private String recoverBinaryContent(String frase) {
		String fraseBinaria = "";
		String sBinario;
		char c;
		
		Character cZeros = frase.charAt(frase.length() - 1);
		String sZeros = cZeros.toString();
		int zeros = Integer.parseInt(sZeros);
		frase = frase.substring(0, frase.length() - 1);

		for (int i = 0; i < frase.length(); i++) {
			c = frase.charAt(i);
			sBinario = Integer.toBinaryString(c);
			while (sBinario.length() < 8) {
				sBinario = "0" + sBinario;
			}
			fraseBinaria = fraseBinaria + sBinario;
		}
		fraseBinaria = fraseBinaria.substring(0, fraseBinaria.length() - zeros);
		return fraseBinaria;
	}

	public String buildContent() {
		int direction;
		HuffmanNode node = rootNode;

		for (int i = 0; i < phrase.length(); i++) {
			direction = Integer.parseInt(phrase.charAt(i) + "");

			// right
			if (direction == 1) {
				node = node.getRight();
				if (node instanceof HuffmanLeaf) {
					HuffmanLeaf leaf = (HuffmanLeaf) node;
					decompressedContent += leaf.getValue();
					node = nodesQueue.peek();
				}
			// left
			} else if (direction == 0) {
				node = node.getLeft();
				if (node instanceof HuffmanLeaf) {
					HuffmanLeaf leaf = (HuffmanLeaf) node;
					decompressedContent += leaf.getValue();
					node = nodesQueue.peek();
				}
			}
		}
		return decompressedContent;
	}
}
